/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.encrypt.utils.EncryptUtils;
import com.comac.ins.common.excel.utils.ExcelUtil;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.isfd.constant.enums.IsfdMetadataTableNameEnum;
import com.comac.ins.isfd.domain.IsfdCompositeMaterial;
import com.comac.ins.isfd.domain.IsfdLayupEquivalentMaterial;
import com.comac.ins.isfd.domain.IsfdLayupSkinPropertie;
import com.comac.ins.isfd.domain.bo.IsfdLayupSkinPropertieBo;
import com.comac.ins.isfd.domain.bo.query.IsfdLayupSkinPropertieQueryBo;
import com.comac.ins.isfd.domain.vo.IsfdLayupSkinPropertieVo;
import com.comac.ins.isfd.mapper.IsfdCompositeMaterialMapper;
import com.comac.ins.isfd.mapper.IsfdLayupEquivalentMaterialMapper;
import com.comac.ins.isfd.mapper.IsfdLayupSkinPropertieMapper;
import com.comac.ins.isfd.service.IIsfdLayupSkinPropertieService;
import com.comac.ins.isfd.service.IIsfdMetadataImportService;
import com.comac.ins.isfd.util.LayupCalculateUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

/**
 * 【请填写功能名称】Service业务层处理
 *
 * @author Lion Li
 * @date 2024-08-02
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class IsfdLayupSkinPropertieServiceImpl implements IIsfdLayupSkinPropertieService, IIsfdMetadataImportService {

    @Autowired
    private IsfdLayupSkinPropertieMapper baseMapper;
    @Autowired
    private IsfdLayupSkinPropertieMapper isfdLayupSkinPropertieMapper;
    @Autowired
    private IsfdLayupEquivalentMaterialMapper isfdLayupEquivalentMaterialMapper;
    @Autowired
    private IsfdCompositeMaterialMapper isfdCompositeMaterialMapper;

    /**
     * 查询【请填写功能名称】
     *
     * @param id 主键
     * @return 【请填写功能名称】
     */
    @Override
    public IsfdLayupSkinPropertieVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询【请填写功能名称】列表
     *
     * @param bo        查询条件
     * @return 【请填写功能名称】分页列表
     */
    @Override
    public TableDataInfo<IsfdLayupSkinPropertieVo> queryPageList(IsfdLayupSkinPropertieQueryBo bo) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(bo.getPageNum());
        pageQuery.setPageSize(bo.getPageSize());

        Wrapper<IsfdLayupSkinPropertie> wrapper = buildWrappers(bo);
        Page<IsfdLayupSkinPropertieVo> result = baseMapper.queryPageList(pageQuery.build(), wrapper);
        return TableDataInfo.build(result);
    }

    protected Wrapper<IsfdLayupSkinPropertie> buildWrappers(IsfdLayupSkinPropertieQueryBo bo) {
        QueryWrapper<IsfdLayupSkinPropertie> lqw = Wrappers.query();
        lqw.like(StringUtils.isNotBlank(bo.getPlyCode()), "lsp.ply_code", bo.getPlyCode())
            .eq(StringUtils.isNotBlank(bo.getMaterial()), "lsp.material", bo.getMaterial())
            .eq(StringUtils.isNotBlank(bo.getSymmetrical()), "lsp.symmetrical", bo.getSymmetrical())
            .eq(bo.getHasCustomPly() != null, "lsp.has_custom_ply", bo.getHasCustomPly())
            .in(CollectionUtil.isNotEmpty(bo.getMetadataManagementIds()), "lsp.metadata_management_id", bo.getMetadataManagementIds())
            .orderByDesc("lsp.create_time");
        return lqw;
    }

    @Transactional
    @Override
    public Boolean save(IsfdLayupSkinPropertieBo bo) {
        // 参数校验
        checkParams(bo);

        // 转换数据格式
        ObjectMapper mapper = new ObjectMapper();
        String dataJson = null;
        try{
            dataJson = mapper.writeValueAsString(bo.getPlyData()); // key 顺序默认按插入顺序
        } catch (Exception e) {
            log.error("铺层数据格式错误！请检查数据" + e);
            throw new BaseException("铺层数据格式错误！请检查数据" + e);
        }

        // plyCode校验唯一
        Boolean isUpdate = checkPlyCode(bo);

        // 属性校验唯一
        String hashUniqueKey = EncryptUtils.hashFields(bo.getMaterial(), bo.getThickness(),
            bo.getSymmetrical(), dataJson);
        checkUnique(bo, hashUniqueKey);

        // PCOMP编号校验唯一
        String PCOMPCode = checkPCOMPCode(bo, isUpdate);

        // 计算等效材料属性. 只有不含自定义层时，才计算
        double[] equivalentMaterialProperties = null;
        IsfdLayupEquivalentMaterial isfdLayupEquivalentMaterial = new IsfdLayupEquivalentMaterial();
        if (bo.getHasCustomPly() == 0) {
            equivalentMaterialProperties = this.calculate(bo);
            isfdLayupEquivalentMaterial.setExx(equivalentMaterialProperties[0]);
            isfdLayupEquivalentMaterial.setEyy(equivalentMaterialProperties[1]);
            isfdLayupEquivalentMaterial.setGxy(equivalentMaterialProperties[2]);
            isfdLayupEquivalentMaterial.setT(equivalentMaterialProperties[3]);
            isfdLayupEquivalentMaterial.setRho(equivalentMaterialProperties[4]);
            isfdLayupEquivalentMaterial.setX(equivalentMaterialProperties[5]);
            isfdLayupEquivalentMaterial.setY(equivalentMaterialProperties[6]);
            isfdLayupEquivalentMaterial.setXy(BigDecimal.valueOf(equivalentMaterialProperties[7]));
            isfdLayupEquivalentMaterial.setPcompNumber(PCOMPCode);
        }

        // 落表
        // 转换保存实体
        IsfdLayupSkinPropertie isfdLayupSkinPropertie = MapstructUtils.convert(bo, IsfdLayupSkinPropertie.class);
        isfdLayupSkinPropertie.setHashUniqueKey(hashUniqueKey);
        isfdLayupSkinPropertie.setData(dataJson);
        // 判断是新增业务还是修改业务，处理落表的方式不同
        if (isUpdate) {
            isfdLayupSkinPropertieMapper.updateById(isfdLayupSkinPropertie);
            // 只有不含自定义层时，才更新
            IsfdLayupEquivalentMaterial existing = isfdLayupEquivalentMaterialMapper.selectOne(
                new LambdaQueryWrapper<IsfdLayupEquivalentMaterial>()
                    .eq(IsfdLayupEquivalentMaterial::getPlyCode, bo.getPlyCode())
            );
            if (bo.getHasCustomPly() == 0) {
                // 没有自定义层 → 子表必须存在或新建
                if (existing == null) {
                    // 新建
                    isfdLayupEquivalentMaterial.setPlyCode(bo.getPlyCode());
                    isfdLayupEquivalentMaterial.setPcompNumber(PCOMPCode);
                    isfdLayupEquivalentMaterialMapper.insert(isfdLayupEquivalentMaterial);
                }else {
                    // 更新
                    isfdLayupEquivalentMaterial.setId(existing.getId());
                    isfdLayupEquivalentMaterial.setPlyCode(bo.getPlyCode());
                    isfdLayupEquivalentMaterial.setPcompNumber(PCOMPCode);
                    isfdLayupEquivalentMaterialMapper.updateById(isfdLayupEquivalentMaterial);
                }
            } else {
                // 有自定义层 → 子表必须不存在
                if (existing != null) {
                    isfdLayupEquivalentMaterialMapper.deleteById(existing.getId());
                }
            }

        } else {
            isfdLayupSkinPropertieMapper.insert(isfdLayupSkinPropertie);
            // 只有不含自定义层时，才新增
            if (bo.getHasCustomPly() == 0) {
                isfdLayupEquivalentMaterial.setPlyCode(isfdLayupSkinPropertie.getPlyCode());
                isfdLayupEquivalentMaterial.setPcompNumber(PCOMPCode);
                isfdLayupEquivalentMaterialMapper.insert(isfdLayupEquivalentMaterial);
            }
        }

        return Boolean.TRUE;
    }

    public double[] calculate(IsfdLayupSkinPropertieBo bo) {
        // 判断材料ID是否存在
        IsfdCompositeMaterial material = checkMaterialCode(bo.getMaterial());

        double[] compositeMaterialProperties = {material.getE1(), material.getE2(), material.getNu12(),
            material.getG12(), material.getRho(),
            material.getLongitudinalCteValue(), material.getTransverseCteValue()};
        double[] equivalentMaterialProperties = new double[8];
        // 提取铺层序列
        List<IsfdLayupSkinPropertieBo.plyData> plyDatas = bo.getPlyData().values().stream().toList();
        List<String> layupSequences = plyDatas.stream()
            .map(plyData -> plyData.getValue().toString())
                .toList();
        LayupCalculateUtils.calculateEquivalentMaterial(bo.getPlyCode(),
            bo.getThickness(),
            bo.getSymmetrical(),
            String.join(",",layupSequences),
            compositeMaterialProperties,
            equivalentMaterialProperties);

        return equivalentMaterialProperties;
    }


    private void checkParams(IsfdLayupSkinPropertieBo bo) {
        if (StringUtils.isBlank(bo.getPlyCode())) {
            throw new BaseException("铺层代码不能为空，请确认后重试！");
        }
        if (StringUtils.isBlank(bo.getMaterial())) {
            throw new BaseException("材料编号不能为空，请确认后重试！");
        }
        if (bo.getThickness() == null) {
            throw new BaseException("thickness不能为空，请确认后重试！");
        }
        if (bo.getSymmetrical() == null) {
            throw new BaseException("是否对称不能为空，请确认后重试！");
        }
        if (bo.getPlyData() == null || bo.getPlyData().isEmpty()) {
            throw new BaseException("铺层序列不能为空，请确认后重试！");
        }
        bo.setHasCustomPly(0);

        // 校验材料
        checkMaterialCode(bo.getMaterial());

        // ===== 新增 Map 内部数据校验 =====
        for (Map.Entry<String, IsfdLayupSkinPropertieBo.plyData> entry : bo.getPlyData().entrySet()) {
            String plyKey = entry.getKey();
            IsfdLayupSkinPropertieBo.plyData plyData = entry.getValue();

            if (plyData == null) {
                throw new BaseException("铺层 " + plyKey + " 数据为空，请确认后重试！");
            }

            if (plyData.getValue() == null) {
                throw new BaseException("铺层 " + plyKey + " 的 序列 不能为空，请确认后重试！");
            }

            IsfdLayupSkinPropertieBo.customObject custom = plyData.getCustom();
            if (custom != null) { // custom 可为空
                bo.setHasCustomPly(1);
                //如果不为空要判断
                if (StringUtils.isBlank(custom.getMaterial())) {
                    throw new BaseException("铺层 " + plyKey + " 的自定义材料不能为空，请确认后重试！");
                }
                // 校验材料是否存在
                checkCustomMaterialCode(custom.getMaterial());
                if (custom.getThickness() == null) {
                    throw new BaseException("铺层 " + plyKey + " 的自定义厚度不能为空，请确认后重试！");
                }
            }
        }
    }

    private IsfdCompositeMaterial checkMaterialCode(String matCode) {
        IsfdCompositeMaterial isfdCompositeMaterial = isfdCompositeMaterialMapper.selectOne(new LambdaQueryWrapper<IsfdCompositeMaterial>()
            .eq(IsfdCompositeMaterial::getMaterialNumber, matCode));
        if (isfdCompositeMaterial == null) {
            throw new BaseException("不存在该复合材料，请确认材料编号！");
        }
        return isfdCompositeMaterial;
    }

    private void checkCustomMaterialCode(String matCode) {
        Integer exist = isfdCompositeMaterialMapper.checkMaterialExists(matCode);
        if (exist == null || exist == 0) {
            throw new BaseException("不存在该材料，请确认材料编号！");
        }
    }

    private String checkPCOMPCode(IsfdLayupSkinPropertieBo bo, Boolean isUpdate) {
        String matCode = bo.getMaterial();
        String plyCode = bo.getPlyCode();
        int matCodeInt = Integer.parseInt(matCode);
        int plyCodeInt = Integer.parseInt(plyCode);
        int pcompCode = (matCodeInt / 10000 * 100000) + plyCodeInt;

        // 查是否有相同的 PCOMP 编号
        LambdaQueryWrapper<IsfdLayupEquivalentMaterial> wrapper =
            new LambdaQueryWrapper<IsfdLayupEquivalentMaterial>()
                .eq(IsfdLayupEquivalentMaterial::getPcompNumber, String.valueOf(pcompCode));

        if (isUpdate) {
            // 修改时排除自己
            wrapper.ne(IsfdLayupEquivalentMaterial::getPlyCode, bo.getPlyCode());
        }

        IsfdLayupEquivalentMaterial conflict = isfdLayupEquivalentMaterialMapper.selectOne(wrapper);
        if (conflict != null) {
            throw new BaseException("PCOMP编号重复，请修改铺层编号！");
        }

        return String.valueOf(pcompCode);
    }

    private Boolean checkPlyCode(IsfdLayupSkinPropertieBo bo) {
        Boolean isUpdate = Boolean.FALSE;
        Long id = bo.getId();
        String plyCode = bo.getPlyCode();
        if (id == null) {
            // 新增时校验 plyCode 唯一
            IsfdLayupSkinPropertie exist = isfdLayupSkinPropertieMapper.selectOne(
                new LambdaQueryWrapper<IsfdLayupSkinPropertie>()
                    .eq(IsfdLayupSkinPropertie::getPlyCode, plyCode)
            );
            if (exist != null) {
                throw new BaseException("plyCode已经存在，不可重复存入，请确认入参后重试！");
            }
        } else {
            // 修改时校验数据是否存在
            IsfdLayupSkinPropertie old = isfdLayupSkinPropertieMapper.selectById(id);
            if (old == null) {
                throw new BaseException("修改失败，数据不存在，请确认id后重试！");
            }

            // 如果修改了 plyCode，校验是否与其他记录冲突
            if (!plyCode.equals(old.getPlyCode())) {
                IsfdLayupSkinPropertie conflict = isfdLayupSkinPropertieMapper.selectOne(
                    new LambdaQueryWrapper<IsfdLayupSkinPropertie>()
                        .eq(IsfdLayupSkinPropertie::getPlyCode, plyCode)
                        .ne(IsfdLayupSkinPropertie::getId, id)
                );
                if (conflict != null) {
                    throw new BaseException("plyCode已经存在，不可重复存入，请确认入参后重试！");
                }
            }
            isUpdate = Boolean.TRUE;
        }
        return isUpdate;
    }


    @Override
    public Boolean checkUnique(IsfdLayupSkinPropertieBo bo, String hashUniqueKey) {
        Boolean isUpdate = Boolean.FALSE;
        Long id = bo.getId();

        if (id == null) {
            // 新增时：直接查是否已存在相同 key
            IsfdLayupSkinPropertie exist = isfdLayupSkinPropertieMapper.selectOne(
                new LambdaQueryWrapper<IsfdLayupSkinPropertie>()
                    .eq(IsfdLayupSkinPropertie::getHashUniqueKey, hashUniqueKey)
            );
            if (exist != null) {
                throw new BaseException("已经存在相同属性蒙皮，不可重复存入，请确认入参后重试！");
            }
        } else {
            // 修改时：先查是否存在
            IsfdLayupSkinPropertie old = isfdLayupSkinPropertieMapper.selectById(id);
            if (old == null) {
                throw new BaseException("修改失败，数据不存在，请确认id后重试！");
            }
            // 如果 key 没变 → 放行
            if (!hashUniqueKey.equals(old.getHashUniqueKey())) {
                // 如果 key 变了 → 查是否有别的记录冲突
                IsfdLayupSkinPropertie conflict = isfdLayupSkinPropertieMapper.selectOne(
                    new LambdaQueryWrapper<IsfdLayupSkinPropertie>()
                        .eq(IsfdLayupSkinPropertie::getHashUniqueKey, hashUniqueKey)
                        .ne(IsfdLayupSkinPropertie::getId, id)
                );
                if (conflict != null) {
                    throw new BaseException("已经存在相同属性蒙皮，不可重复存入，请确认入参后重试！");
                }
            }
            isUpdate = Boolean.TRUE;
        }
        return isUpdate;
    }

    /**
     * 校验并批量删除【请填写功能名称】信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public void export(IsfdLayupSkinPropertieQueryBo queryBo, HttpServletResponse response) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(1);
        pageQuery.setPageSize(9999);
        Wrapper<IsfdLayupSkinPropertie> wrapper = buildWrappers(queryBo);
        Page<IsfdLayupSkinPropertieVo> result = baseMapper.queryPageList(pageQuery.build(), wrapper);
        List<IsfdLayupSkinPropertieVo> list = result.getRecords();

        // data字段需要重新构造导出的列
        // 确定最大的PLY列数
        ObjectMapper objectMapper = new ObjectMapper();
        int maxPly = list.stream()
            .mapToInt(vo -> {
                try {
                    if (vo.getData() == null) {
                        return 0;
                    }
                    Map<String, IsfdLayupSkinPropertieBo.plyData> map =
                        objectMapper.readValue(vo.getData(),
                            new TypeReference<Map<String, IsfdLayupSkinPropertieBo.plyData>>() {});
                    return map.size();
                } catch (Exception e) {
                    // 如果 JSON 解析失败，就认为是 0
                    return 0;
                }
            })
            .max()
            .orElse(0);

        // 构建动态表头：原导出字段 + PLY1到PLYN
        List<String> headerList = new ArrayList<>();
        headerList.add("铺层代码");
        headerList.add("是否对称铺层");
        for (int i = 1; i <= maxPly; i++) {
            headerList.add("PLY" + i);
        }

        // 转换数据为Map列表
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (IsfdLayupSkinPropertieVo vo : list) {
            Map<String, Object> rowMap = new HashMap<>();
            // 添加原导出字段
            rowMap.put("铺层代码", vo.getPlyCode());
            rowMap.put("是否对称铺层", vo.getSymmetrical());

            Map<String, IsfdLayupSkinPropertieBo.plyData> map = new HashMap<>();
            try {
                map = objectMapper.readValue(
                    vo.getData(),
                    new TypeReference<Map<String, IsfdLayupSkinPropertieBo.plyData>>() {}
                );
            } catch (IOException e) {
                // 记录日志，避免直接吞掉异常
                log.error("Failed to parse JSON data to Map<String, plyData>, data={}", vo.getData(), e);
                // 可以选择抛出业务异常，避免后续使用 map 时出错
                throw new RuntimeException("数据解析失败，请检查格式是否正确", e);
            }

            // 拆分data字段到PLY列
            String[] plyValues = map.values().stream()
                .map(ply -> String.valueOf(ply.getValue()))
                .toArray(String[]::new);

            for (int i = 0; i < maxPly; i++) {
                String plyKey = "PLY" + (i + 1);
                rowMap.put(plyKey, i < plyValues.length ? plyValues[i] : "");
            }
            dataList.add(rowMap);
        }

        ExcelUtil.exportExcelReBuildHeader("【铺层库蒙皮属性】", headerList, dataList, response);
    }

    @Override
    public String selectByTableName() {
        return IsfdMetadataTableNameEnum.ISFD_LAYUP_SKIN_PROPERTIE.getValue();
    }

    @Override
    public Long excelImport(File excelImportFile, String metadataManagementId) {
        long successCount = 0L;

        try (InputStream is = new FileInputStream(excelImportFile)) {
            List<IsfdLayupSkinPropertieVo> targetVo = ExcelUtil.importExcel(is, IsfdLayupSkinPropertieVo.class);
            for (IsfdLayupSkinPropertieVo vo : targetVo) {
                IsfdLayupSkinPropertieBo bo = new IsfdLayupSkinPropertieBo();
                bo.setPlyCode(vo.getPlyCode());
                bo.setMaterial(vo.getMaterial());
                bo.setThickness(vo.getThickness());
                bo.setSymmetrical(vo.getSymmetrical());
                String dataJson = vo.getData(); // VO 里是 String
                Map<String, IsfdLayupSkinPropertieBo.plyData> plyDataMap = new HashMap<>();
                ObjectMapper objectMapper = new ObjectMapper();
                if (StringUtils.isNotBlank(dataJson)) {
                    plyDataMap = objectMapper.readValue(dataJson, new TypeReference<>() {});
                }
                bo.setPlyData(plyDataMap);
                try{
                    bo.setMetadataManagementId(metadataManagementId);
                    this.save(bo);
                    successCount++;
                } catch (Exception e) {
                    log.error("铺层数据导入失败", e);
                }
            }

        } catch (Exception e) {
            log.error("sheet文件转换为实体类失败", e);
            throw new BaseException("sheet文件转换为实体类失败");
        }

        return successCount;
    }



}
